﻿import asyncio
import smtplib
from email.message import EmailMessage
from typing import Optional

import structlog

from app.core.config import settings

logger = structlog.get_logger('love_notes.notification')


class NotificationService:
  def __init__(self) -> None:
    self.enabled = all(
      [
        settings.smtp_host,
        settings.email_sender,
        settings.smtp_username,
        settings.smtp_password,
      ]
    )

  async def send_email(self, *, to: str, subject: str, body: str, html_body: str | None = None) -> bool:
    if not self.enabled:
      logger.info('Email service is not configured; skip sending', to=to, subject=subject)
      return False

    message = EmailMessage()
    message['From'] = settings.email_sender
    message['To'] = to
    message['Subject'] = subject
    if html_body:
      message.set_content(body)
      message.add_alternative(html_body, subtype='html')
    else:
      message.set_content(body)

    try:
      await asyncio.to_thread(self._send_sync, message)
      logger.info('Email sent', to=to, subject=subject)
      return True
    except Exception:
      # 任何 SMTP 异常都记录日志并返回 False，不要向上抛出以免阻塞业务流程
      logger.exception('Failed to send email', to=to, subject=subject)
      return False

  def _send_sync(self, message: EmailMessage) -> None:
    host = settings.smtp_host
    port = settings.smtp_port
    username = settings.smtp_username
    password = settings.smtp_password
    use_tls = settings.smtp_use_tls

    def _send_with_ssl() -> None:
      smtp = smtplib.SMTP_SSL(host, port, timeout=15)
      try:
        smtp.login(username, password)
        smtp.send_message(message)
        try:
          smtp.quit()
        except smtplib.SMTPResponseException as e:  # 常见于服务器主动断开，但邮件已送达
          logger.warning('SMTP quit warning (ignored)', code=getattr(e, 'smtp_code', None), message=getattr(e, 'smtp_error', None))
      finally:
        try:
          smtp.close()
        except Exception:
          pass

    def _send_with_starttls() -> None:
      smtp = smtplib.SMTP(host, port, timeout=15)
      try:
        smtp.starttls()
        smtp.login(username, password)
        smtp.send_message(message)
        try:
          smtp.quit()
        except smtplib.SMTPResponseException as e:
          logger.warning('SMTP quit warning (ignored)', code=getattr(e, 'smtp_code', None), message=getattr(e, 'smtp_error', None))
      finally:
        try:
          smtp.close()
        except Exception:
          pass

    try:
      if use_tls and port not in (465, 994):
        _send_with_starttls()
      else:
        _send_with_ssl()
    except smtplib.SMTPServerDisconnected:
      logger.warning('SMTP disconnected unexpectedly; retry with SSL', host=host, port=port)
      _send_with_ssl()
    except smtplib.SMTPException:
      logger.exception('SMTP error while sending email', host=host, port=port)
      # 交由外层 send_email 捕获并吞掉
      raise

  async def notify_partner_diary(
    self,
    *,
    partner_email: Optional[str],
    author_name: str,
    title: str,
    summary: str | None,
    link: str | None = None,
  ) -> None:
    if not partner_email:
      logger.info('Skip diary notification: partner email missing')
      return

    lines = [
      f'亲爱的，这是一条来自 {author_name} 的新日记提醒：',
      f'标题：{title}',
    ]
    if summary:
      lines.append('AI 摘要：')
      lines.append(summary)
    if link:
      lines.append(f'👉 点击查看：{link}')
    lines.append('\n如果不想再收到此类提醒，可在设置中关闭邮件通知。')

    await self.send_email(to=partner_email, subject=f'{author_name} 刚刚写下了一篇日记', body='\n'.join(lines))


notification_service = NotificationService()
